package cn.gf.dimanagementchartdatatransfor.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 自定义异步线程池,异步类
 */
@Configuration
@EnableAsync
@Slf4j
public class MyAsyncConfig implements AsyncConfigurer {
	@Bean(name = "myAsyncThreadPool")
	public ThreadPoolTaskExecutor getMyExecutor() {
		ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
		//核心线程数
		taskExecutor.setCorePoolSize(3);
		//线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程
		taskExecutor.setMaxPoolSize(10);
		//缓存队列
		taskExecutor.setQueueCapacity(20);
		// 设置线程的空闲时间,当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
		taskExecutor.setKeepAliveSeconds(120);
		/**
		 * 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize，如果还有任务到来就会采取任务拒绝策略
		 * 通常有以下四种策略：
		 * ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
		 * ThreadPoolExecutor.DiscardPolicy：也是丢弃任务，但是不抛出异常。
		 * ThreadPoolExecutor.DiscardOldestPolicy：丢弃队列最前面的任务，然后重新尝试执行任务（重复此过程）
		 * ThreadPoolExecutor.CallerRunsPolicy：重试添加当前的任务，自动重复调用 execute() 方法，直到成功
		 */
		taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
		//异步方法内部线程名称
		taskExecutor.setThreadNamePrefix("my-async-thread-pool-");
		taskExecutor.initialize();
		return taskExecutor;
	}

	/**
	 * 指定默认线程池
	 */
	@Override
	public Executor getAsyncExecutor() {
		// 将默认的线程池替换为自定义的线程池
		return getMyExecutor();
	}

	/**
	 * 该函数用于设置一个异步方法执行时抛出异常时的未捕获异常处理器实例。当异步方法返回类型为void时，此处理器将被用于处理异常。
	 */
	@Override
	public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
		return (ex, method, params) -> log.error("线程池执行任务发送未知错误, 执行方法：{}", method.getName(), ex);

	}
}
